package com.e3roid.examples;

import android.hardware.SensorManager;

import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.physics.box2d.Body;
import com.badlogic.gdx.physics.box2d.BodyDef;
import com.badlogic.gdx.physics.box2d.BodyDef.BodyType;
import com.badlogic.gdx.physics.box2d.FixtureDef;
import com.badlogic.gdx.physics.box2d.PolygonShape;
import com.e3roid.E3Activity;
import com.e3roid.E3Engine;
import com.e3roid.E3Scene;
import com.e3roid.drawable.Shape;
import com.e3roid.drawable.controls.DigitalController;
import com.e3roid.drawable.controls.StickController;
import com.e3roid.drawable.sprite.AnimatedSprite;
import com.e3roid.drawable.texture.AssetTexture;
import com.e3roid.drawable.texture.TiledTexture;
import com.e3roid.event.ControllerEventListener;
import com.e3roid.event.SceneUpdateListener;
import com.e3roid.physics.PhysicsShape;
import com.e3roid.physics.PhysicsWorld;
import com.e3roid.util.Debug;
import com.e3roid.util.FPSListener;
import com.e3roid.util.MathUtil;

/*
 *  This class shows the example of bouncing the dog using physics.
 *  Some of the functionality was inspired by the code by Nicolas Gramlich from AndEngine(www.andengine.org).
 */
public class PhysicsBounceExample extends E3Activity implements FPSListener, ControllerEventListener, SceneUpdateListener {

	private final static int WIDTH  = 320;
	private final static int HEIGHT = 480;
	
	private AnimatedSprite dogSprite;
	private TiledTexture dogTexture;
	private AssetTexture controlBaseTexture;
	private AssetTexture controlKnobTexture;
	private PhysicsWorld world;
	private PhysicsShape dogPhyShape;
	
	private Shape bar;
	private static final float CONTROL_VECTOR = SensorManager.GRAVITY_MOON / 2;

	@Override
	public E3Engine onLoadEngine() {
		E3Engine engine = new E3Engine(this, WIDTH, HEIGHT);
		engine.requestFullScreen();
		engine.requestPortrait();
		return engine;
	}

	@Override
	public E3Scene onLoadScene() {
		E3Scene scene = new E3Scene();
		scene.registerUpdateListener(60, this);
		scene.registerUpdateListener(60, world);
		engine.getFPSCounter().addListener(this);		
		
		DigitalController controller = new DigitalController(
				controlBaseTexture, controlKnobTexture,
				0, getHeight() - controlBaseTexture.getHeight(), scene, this);
		controller.setAlpha(0.7f);
		scene.addHUD(controller);
		scene.addEventListener(controller);
		
		// create physics box
		int bodySize = 2;
		Shape ground = new Shape(0, getHeight() - bodySize, getWidth(), bodySize);
		Shape left   = new Shape(0, 0, bodySize, getHeight());
		Shape right  = new Shape(getWidth() - bodySize, 0, bodySize, getHeight());
		
		final FixtureDef wallFixtureDef = createFixtureDef(0.0f, 0.5f, 0.5f);
		createBoxBody(this.world, ground, BodyType.StaticBody, wallFixtureDef);
		createBoxBody(this.world, left, BodyType.StaticBody, wallFixtureDef);
		createBoxBody(this.world, right, BodyType.StaticBody, wallFixtureDef);
		
		scene.getTopLayer().add(ground);
		scene.getTopLayer().add(left);
		scene.getTopLayer().add(right);
		
		bar = new Shape(getWidth() / 4, getHeight() - 50, getWidth() / 2, 10);
		bar.setColor(1, 0, 0);
		scene.getTopLayer().add(bar);
		
		scene.getTopLayer().add(dogSprite);
		
		scene.setBackgroundColor(0.94f, 1.00f, 0.94f, 1);
		
		return scene;
	}
	
	@Override
	public void onLoadResources() {
		controlBaseTexture = new AssetTexture("controller_base.png", this);
		controlKnobTexture = new AssetTexture("controller_knob.png", this);
		
		world = new PhysicsWorld(new Vector2(0, SensorManager.GRAVITY_MOON), false);
		
		dogTexture = new TiledTexture("dog.png", 34, 42, 0, 0, 1, this);
		dogSprite = new AnimatedSprite(dogTexture, getWidth() / 2, getHeight() / 2);
		
		FixtureDef objectFixtureDef = createFixtureDef(1.0f, 1.0f, 0.5f);
		dogPhyShape = new PhysicsShape(dogSprite, createBoxBody(
				world, dogSprite, BodyType.DynamicBody, objectFixtureDef));
		dogPhyShape.enableUpdateRotation(false);
		world.addShape(dogPhyShape);
	}
	
	@Override
	public void onFPS(float fps, float minFPS, float maxFPS) {
		Debug.d(String.format("FPS: %.2f (MIN %.2f / MAX %.2f)", fps, minFPS, maxFPS));
	}

	@Override
	public void onControlUpdate(StickController controller, int relativeX,
			int relativeY, boolean hasChanged) {
		int dir = controller.getDirection();
		Vector2 currentVelocity = dogPhyShape.getBody().getLinearVelocity();
		if (hasChanged) {
			if (dir == StickController.LEFT) {
				dogSprite.setTile(0, 1);
				dogPhyShape.getBody().setLinearVelocity(new Vector2(-CONTROL_VECTOR, 0));
			} else if (dir == StickController.RIGHT) {
				dogSprite.setTile(0, 0);
				dogPhyShape.getBody().setLinearVelocity(new Vector2(CONTROL_VECTOR, 0));
			}
		}
		if (dir == StickController.DOWN) {
			dogPhyShape.getBody().setLinearVelocity(currentVelocity.add(0, CONTROL_VECTOR));
		}
	}

	@Override
	public void onUpdateScene(E3Scene scene, long elapsedMsec) {
		if (dogSprite.collidesWith(bar)) {
			Vector2 currentVelocity = dogPhyShape.getBody().getLinearVelocity();
			dogPhyShape.getBody().setLinearVelocity(currentVelocity.add(0, -CONTROL_VECTOR));
		}
	}

	private FixtureDef createFixtureDef(float density, float restitution, float friction) {
		FixtureDef fixtureDef = new FixtureDef();
		fixtureDef.density = density;
		fixtureDef.restitution = restitution;
		fixtureDef.friction = friction;
		fixtureDef.isSensor = false;
		return fixtureDef;
	}

	private Body createBoxBody(PhysicsWorld physicsWorld, Shape shape,
			BodyType bodyType, FixtureDef fixtureDef) {
		float pixelToMeterRatio = PhysicsWorld.PIXEL_TO_METER_RATIO_DEFAULT;
		BodyDef boxBodyDef = new BodyDef();
		boxBodyDef.type = bodyType;

		float[] sceneCenterCoordinates = shape.getGlobalCenterCoordinates();
		boxBodyDef.position.x = sceneCenterCoordinates[0] / (float)pixelToMeterRatio;
		boxBodyDef.position.y = sceneCenterCoordinates[1] / (float)pixelToMeterRatio;

		Body boxBody = physicsWorld.createBody(boxBodyDef);
		PolygonShape boxPoly = new PolygonShape();

		float halfWidth = shape.getWidthScaled() * 0.5f / pixelToMeterRatio;
		float halfHeight = shape.getHeightScaled() * 0.5f / pixelToMeterRatio;

		boxPoly.setAsBox(halfWidth, halfHeight);
		fixtureDef.shape = boxPoly;
		boxBody.createFixture(fixtureDef);
		boxPoly.dispose();
		
		boxBody.setTransform(boxBody.getWorldCenter(), MathUtil.degToRad(shape.getAngle()));

		return boxBody;
	}

}
